﻿using System;
using System.Collections.Generic;
using System.Diagnostics;

namespace System {
	/// <summary>
	/// Element for accesing objects.
	/// </summary>
	#if !SILVERLIGHT
	[Serializable()]
	#endif
	public sealed class PackageProperty {
		#region Fields
		public readonly String Name;
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public readonly Int32 LocalIndex;
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public readonly Int32 GlobalIndex;
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public readonly Boolean ReadOnly;


		public readonly Type OwnerType;
		public readonly Type PropertyType;

		public readonly PackageMetadata Metadata;
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		private static Dictionary<Type, PackageProperty[]> _localValues = new Dictionary<Type, PackageProperty[]>();
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		private static Int32 GlobalIncrement = 1;
		#endregion
		#region Constructor
		/// <summary>
		/// Initializes a new instance of the <see cref="PackageProperty"/> class.
		/// </summary>
		private PackageProperty() {

		}
		/// <summary>
		/// Initializes a new instance of the <see cref="PackageProperty"/> class.
		/// </summary>
		/// <param name="name">The name.</param>
		/// <param name="propertyType">Type of the property.</param>
		/// <param name="ownerType">Type of the owner.</param>
		/// <param name="localIndex">Index of the local.</param>
		/// <param name="globalIndex">Index of the global.</param>
		/// <param name="meta">The meta.</param>
		private PackageProperty(String name, Type propertyType, Type ownerType, Int32 localIndex, Int32 globalIndex, PackageMetadata meta) {
			this.Name = name;
			this.OwnerType = ownerType;
			this.PropertyType = propertyType;
			this.LocalIndex = localIndex;
			this.GlobalIndex = globalIndex;
			this.Metadata = meta;
			this.ReadOnly = false;
		}
		#endregion
		#region Private

		#endregion
		#region Public
		/// <summary>
		/// Returns a <see cref="System.String"/> that represents this instance.
		/// </summary>
		/// <returns>
		/// A <see cref="System.String"/> that represents this instance.
		/// </returns>
		public override string ToString() {
			return this.Name;
		}
		#endregion
		#region Static
		/// <summary>
		/// Registers new property.
		/// </summary>
		/// <param name="name">The name.</param>
		/// <param name="propertyType">Type of the property.</param>
		/// <param name="ownerType">Type of the owner.</param>
		/// <returns></returns>
		public static PackageProperty Register(String name, Type propertyType, Type ownerType) {
			return Register(name, propertyType, ownerType,PackageMetadata.Default);
		}
		/// <summary>
		/// Registers new property.
		/// </summary>
		/// <param name="name">The name.</param>
		/// <param name="propertyType">Type of the property.</param>
		/// <param name="ownerType">Type of the owner.</param>
		/// <param name="meta">The meta.</param>
		/// <returns></returns>
		public static PackageProperty Register(String name, Type propertyType, Type ownerType, PackageMetadata meta) {
			if (String.IsNullOrEmpty(name)) {
				throw new ArgumentNullException("name");
			}
			if (ownerType == null) {
				throw new ArgumentNullException("ownerType");
			}
			if (propertyType == null) {
				throw new ArgumentNullException("propertyType");
			}

			Int32 localIndex = 0;
			Int32 globalIndex = GlobalIncrement;
			GlobalIncrement += 1;

			PackageProperty property = null;
			lock (_localValues) {
				if (!_localValues.ContainsKey(ownerType)) {

					property = new PackageProperty(name, propertyType, ownerType, localIndex, globalIndex, meta);
					_localValues[ownerType] = new PackageProperty[] { property };
				} else {
					PackageProperty[] props = _localValues[ownerType];

					localIndex = props.Length;
					Array.Resize<PackageProperty>(ref props, localIndex + 1);
					property = new PackageProperty(name, propertyType, ownerType, localIndex, globalIndex, meta);
					props[localIndex] = property;
					_localValues[ownerType] = props;
				}
			}
			return property;
		}
		/// <summary>
		/// Registers new property.
		/// </summary>
		/// <typeparam name="TPropertyType">The type of the property type.</typeparam>
		/// <typeparam name="TOwnerType">The type of the owner type.</typeparam>
		/// <param name="name">The name.</param>
		/// <returns></returns>
		[Obsolete()]
		public static PackageProperty Register<TPropertyType, TOwnerType>(String name) {
			return Register(name, typeof(TPropertyType), typeof(TOwnerType));
		}
		/// <summary>
		/// Registers new property.
		/// </summary>
		/// <typeparam name="TPropertyType">The type of the property type.</typeparam>
		/// <typeparam name="TOwnerType">The type of the owner type.</typeparam>
		/// <param name="name">The name.</param>
		/// <param name="defaultValue">The default value.</param>
		/// <returns></returns>
		[Obsolete()]
		public static PackageProperty Register<TPropertyType, TOwnerType>(String name,Object defaultValue) {
			return Register(name, typeof(TPropertyType), typeof(TOwnerType));
		}
		/// <summary>
		/// Gets the property count.
		/// </summary>
		/// <param name="ownerType">Type of the owner.</param>
		/// <returns></returns>
		public static Int32 GetPropertyCount(Type ownerType) {
			return GetPropertyCount(ownerType, false);
		}
		/// <summary>
		/// Gets the property count.
		/// </summary>
		/// <param name="ownerType">Type of the owner.</param>
		/// <param name="includeChildren">if set to <c>true</c> [include children].</param>
		/// <returns></returns>
		public static Int32 GetPropertyCount(Type ownerType,Boolean includeChildren) {
			Int32 count = 0;
			lock (_localValues) {
				if (_localValues.ContainsKey(ownerType)) {
					count = _localValues[ownerType].Length;
				}
			}
			if (includeChildren) {
				if (ownerType.BaseType.IsSubclassOf(typeof(PackageBase))) {
					count += GetPropertyCount(ownerType.BaseType, true);
				}
			}
			return count;
		}

		/// <summary>
		/// Finds the property.
		/// </summary>
		/// <param name="ownerType">Type of the owner.</param>
		/// <param name="list">The list.</param>
		private static void FindProperty(Type ownerType, ref List<PackageProperty> list) {
			lock (_localValues) {
				if (_localValues.ContainsKey(ownerType)) {
					PackageProperty[] arr = _localValues[ownerType];

					if (arr != null && arr.Length > 0) {
						list.AddRange(arr);
					}
				}

				if (ownerType.BaseType.IsSubclassOf(typeof(PackageBase))) {
					FindProperty(ownerType.BaseType, ref list);
				}
			}
		}
		/// <summary>
		/// Gets the properties.
		/// </summary>
		/// <param name="ownerType">Type of the owner.</param>
		/// <returns></returns>
		public static PackageProperty[] GetProperties(Type ownerType) {
			if (ownerType == null) {
				throw new ArgumentNullException("ownerType");
			}

			List<PackageProperty> list = new List<PackageProperty>();
			FindProperty(ownerType, ref list);
			return list.ToArray();
		}
		#endregion
		#region Property

		#endregion
	}
}
